use serde::{Deserialize, Deserializer, Serialize};
use snafu::Snafu;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, Hash)]
pub struct NativeVersion(u32);

#[derive(Debug, Snafu, Serialize)]
pub enum Error {
    #[snafu(display("Failed to parse value {value}, it must be in range [0, 999999]"))]
    OutOfRange { value: i64 },
    #[snafu(display("Failed to parse from string: {s}"))]
    FromString { s: String },
}

impl<'de> Deserialize<'de> for NativeVersion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let v = <u32 as Deserialize>::deserialize(deserializer)?;
        NativeVersion::new(v).map_err(|e| serde::de::Error::custom(e.to_string()))
    }
}

impl Display for NativeVersion {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<NativeVersion> for u32 {
    fn from(value: NativeVersion) -> Self {
        value.0
    }
}

impl FromStr for NativeVersion {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        <u32 as FromStr>::from_str(s)
            .map_err(|_| Error::FromString { s: s.into() })
            .and_then(|n| NativeVersion::new(n))
    }
}

impl NativeVersion {
    pub fn new(version: u32) -> Result<Self, Error> {
        if version > 999999 {
            Err(Error::OutOfRange {
                value: version as i64,
            })
        } else {
            Ok(Self { 0: version })
        }
    }
}
